/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hugegraph.backend.query;

import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.structure.HugeElement;
import org.apache.hugegraph.type.HugeType;
import org.apache.hugegraph.util.Bytes;
import org.apache.hugegraph.util.E;

public final class IdRangeQuery extends Query {

    private final Id start;
    private final Id end;
    private final boolean inclusiveStart;
    private final boolean inclusiveEnd;

    public IdRangeQuery(HugeType resultType, Id start, Id end) {
        this(resultType, null, start, end);
    }

    public IdRangeQuery(HugeType resultType, Query originQuery,
                        Id start, Id end) {
        this(resultType, originQuery, start, true, end, false);
    }

    public IdRangeQuery(Query originQuery,
                        Id start, boolean inclusiveStart,
                        Id end, boolean inclusiveEnd) {
        this(originQuery.resultType(), originQuery,
             start, inclusiveStart, end, inclusiveEnd);
    }

    public IdRangeQuery(HugeType resultType, Query originQuery,
                        Id start, boolean inclusiveStart,
                        Id end, boolean inclusiveEnd) {
        super(resultType, originQuery);
        E.checkArgumentNotNull(start, "The start parameter can't be null");
        this.start = start;
        this.end = end;
        this.inclusiveStart = inclusiveStart;
        this.inclusiveEnd = inclusiveEnd;
        if (originQuery != null) {
            this.copyBasic(originQuery);
        }
    }

    public Id start() {
        return this.start;
    }

    public Id end() {
        return this.end;
    }

    public boolean inclusiveStart() {
        return this.inclusiveStart;
    }

    public boolean inclusiveEnd() {
        return this.inclusiveEnd;
    }

    @Override
    public boolean empty() {
        return false;
    }

    @Override
    public boolean test(HugeElement element) {
        int cmp1 = Bytes.compare(element.id().asBytes(), this.start.asBytes());
        int cmp2 = Bytes.compare(element.id().asBytes(), this.end.asBytes());
        return (this.inclusiveStart ? cmp1 >= 0 : cmp1 > 0) &&
               (this.inclusiveEnd ? cmp2 <= 0 : cmp2 < 0);
    }

    @Override
    public IdRangeQuery copy() {
        return (IdRangeQuery) super.copy();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder(super.toString());
        assert builder.length() > 0;
        builder.deleteCharAt(builder.length() - 1); // Remove the last "`"
        builder.append(" id in range ")
               .append(this.inclusiveStart ? "[" : "(")
               .append(this.start)
               .append(", ")
               .append(this.end)
               .append(this.inclusiveEnd ? "]" : ")")
               .append("`");
        return builder.toString();
    }
}
